#pragma once
#include "DSpace.h"
#include "Geometries/DGeometry.h"
#include "DAabb.h"
#include "DOde.h"

void Space_NearCallback(void *data, dGeomID o1, dGeomID o2)
{	
	Skill::Ode::DIUnmanagedPointer^ p = Skill::Ode::DRegisteredObjects::Get(IntPtr(data));
	if(p)
	{
		Skill::Ode::DSpace^ space = dynamic_cast<Skill::Ode::DSpace^>(p);
		if(space && space->NearCallback != nullptr)
		{
			p = Skill::Ode::DRegisteredObjects::Get(IntPtr(o1));
			Skill::Ode::DSpace^ O1 = dynamic_cast<Skill::Ode::DSpace^>(p);
			p = Skill::Ode::DRegisteredObjects::Get(IntPtr(o2));
			Skill::Ode::DSpace^ O2 = dynamic_cast<Skill::Ode::DSpace^>(p);
			space->NearCallback(space->_CurrenData,O1,O2);
		}
	}
	p = nullptr;
}

namespace Skill
{
	namespace Ode
	{		
		void DSpace::CollectManagedMemory()
		{
			_CurrenData = nullptr;
			_Name = nullptr;
			_UserData = nullptr;
			NearCallback = nullptr;
			if(_Spaces)
			{				
				for each (DSpace^ s in _Spaces->Values)
				{
					DRegisteredObjects::Remove(s);
					s->_Free = false;
					s->_Container = nullptr;
				}
				_Spaces->Clear();
			}				
			_Spaces = nullptr;
			_Aabb = nullptr;
			if(_Container)
				_Container->RemoveSpace(this);
			_Container = nullptr;
		}		
		void DSpace::AddSpace(DSpace^ s)
		{
			GeomKey k = DRegisteredObjects::GetKey(s->UnmanagedPointer);
			if(!_Spaces->ContainsKey(k))
				_Spaces->Add(k,s);
		}
		void DSpace::RemoveSpace(DSpace^ s)
		{
			GeomKey k = DRegisteredObjects::GetKey(s->UnmanagedPointer);			
			_Spaces->Remove(k);
		}

		DSpace::!DSpace()
		{				
			if(_Free && _This && DOde::IsInitialized)
				dSpaceDestroy(_Space());
			_This = nullptr;
			_Free = false;
			_Disposed = true;
		}

			String^ DSpace::Name::get()
			{
				return _Name;
			}
			void DSpace::Name::set(String^ value)
			{
				_Name = value;
			}

			Object^ DSpace::UserData::get()
			{
				return _UserData;
			}
			void DSpace::UserData::set(Object^ value)
			{
				_UserData = value;
			}

		int DSpace::CleanupMode::get()
		{
			if(IsSpace)
				return dSpaceGetCleanup(_Space());
			return Int32::MinValue;
		}
		void DSpace::CleanupMode::set(int value)
		{
			if(IsSpace)
				dSpaceSetCleanup(_Space(),value);
		}

		int DSpace::Sublevel::get()
		{
			if(IsSpace)
				return dSpaceGetSublevel(_Space());
			return Int32::MinValue;
		}
		void DSpace::Sublevel::set(int value)
		{
			if(IsSpace)
				dSpaceSetSublevel(_Space(),value);
		}

		void DSpace::Add(DGeometry^ geom)
		{
			if(!IsSpace)
				return;
			dSpaceAdd(_Space(),geom->_Geom());
			AddSpace(geom);
			geom->_Container = this;
		}
		void DSpace::Remove(DGeometry^ geom)
		{
			if(!IsSpace)
				return;
			dSpaceRemove(_Space(),geom->_Geom());
			RemoveSpace(geom);
			geom->_Container = nullptr;
		}
		int DSpace::Query(DGeometry^ geom)
		{
			if(IsSpace)			
				return dSpaceQuery(_Space(),geom->_Geom());
			return Int32::MinValue;
		}
		void DSpace::Clean()
		{
			if(!IsSpace)
				return;
			dSpaceClean(_Space());
			for each (DGeometry^ g in _Spaces->Values)
			{
				g->_Container = nullptr;
			}
			_Spaces->Clear();
		}
		void DSpace::CleanAndDispose()
		{
			if(!IsSpace)
				return;
			dSpaceClean(_Space());
			for each (DSpace^ s in _Spaces->Values)
			{
				s->_Container = nullptr;
				s->~DSpace();
			}
			_Spaces->Clear();
		}

		int DSpace::NumGeoms::get()
		{
			if(!IsSpace)
				return 0;
			return dSpaceGetNumGeoms(_Space());
		}

		DGeometry^ DSpace::default::get(int i)
		{
			if(!IsSpace)
				return nullptr;
			dGeomID g = dSpaceGetGeom(_Space(),i);
			DGeometry^ Geom = nullptr;
			if(g)			
				_Spaces->TryGetValue(DRegisteredObjects::GetKey(IntPtr(g)),Geom);
			return Geom;
		}

		int DSpace::ClassID::get()
		{
			if(!IsSpace)
				return Int32::MinValue;
			return dSpaceGetClass(_Space());
		}

		DAabb^ DSpace::Aabb::get()
		{
			if(!IsSpace)
				return nullptr;
			if(!_Aabb)
				_Aabb = gcnew DAabb();
			dGeomGetAABB((dGeomID)_This,_Aabb->_This);
			return _Aabb;
		}
		DSpace^ DGeometry::Container::get()
		{
			return _Container;
		}

		void DSpace::Collide(Object^ data)
		{			
			if(!IsSpace)
				return;			
			_CurrenData = data;			
			dSpaceCollide(_Space(),_This,Space_NearCallback);			
		}

		void DSpace::Collide(DSpace^ space2, Object^ data)
		{			
			if(!IsSpace)
				return;			
			_CurrenData = data;
			dSpaceCollide2((dGeomID)_This,(dGeomID)space2->_This,_This,Space_NearCallback);
		}		
	}
}